package sdk.garena;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sdk.common.CryptoUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

public class GarenaOauthApi {

	private static Logger logger = LoggerFactory.getLogger(GarenaOauthApi.class);

	static class SingletonHolder {
		static GarenaOauthApi instance = new GarenaOauthApi();
	}

	public static GarenaOauthApi getInstance() {
		return SingletonHolder.instance;
	}

	public Map<String, Object> exchangeAccessToken(String userAuthCode, String redirectUri, int appId, String appKey)
			throws IOException {
		String requestUrlPath = "/oauth/token";

		// 发送请求参数
		Map<String, String> requestParams = new HashMap<String, String>();
		requestParams.put("grant_type", "authorization_code");
		requestParams.put("code", userAuthCode);
		requestParams.put("redirect_uri", redirectUri);
		requestParams.put("client_id", String.valueOf(appId));
		requestParams.put("client_secret", appKey);

		Map<String, Object> retMap = doHttpPost(SDKConstants.getGarenaUrlBase() + requestUrlPath,
				buildPostParams(requestParams), null);
		return retMap;
	}

	public Map<String, Object> inspectAccessToken(String accessToken) throws IOException {
		String requestUrlPath = "/oauth/token/inspect";

		// 发送请求参数
		String requestParams = String.format("token=%1$s", accessToken);
		Map<String, Object> retMap = doHttpGet(requestUrlPath, requestParams, null);
		return retMap;
	}

	public Map<String, Object> getUserInfo(String accessToken) {
		String requestUrlPath = "/oauth/user/info/get";
		// 发送请求参数
		String requestParams = String.format("access_token=%1$s", accessToken);
		Map<String, Object> retMap = doHttpGet(requestUrlPath, requestParams, null);
		if (retMap.containsKey("error")) {
			logger.warn("error:{}", retMap.get("error"));
		}
		return retMap;
	}

	public Map<String, Object> bindGuestAccount(String newAccessToken, String guestAccessToken) {
		String requestUrlPath = "/game/guest/bind";
		// 发送请求参数
		Map<String, String> requestParams = new HashMap<String, String>();
		requestParams.put("access_token", newAccessToken);
		requestParams.put("guest_access_token", guestAccessToken);

		Map<String, Object> retMap = doHttpPost(SDKConstants.getGarenaUrlBase() + requestUrlPath,
				buildPostParams(requestParams), null);
		return retMap;
	}

	public Map<String, Object> getBalance(String accessToken, int clientType, int appServerId, int appRoleId) {
		String requestUrlPath = "/app/point/get_balance";

		// 发送请求参数
		String requestParams = String.format("access_token=%1$s&client_type=%2$d&app_server_id=%3$d&app_role_id=%4$d",
				accessToken, clientType, appServerId, appRoleId);
		// 生成签名 Signature = HEX(HMACSHA256(Key=payment_key, Message=http_body))
		String signatureStr = "";
		try {
			signatureStr = CryptoUtils.getInstance().hmacSha256ToHex(requestParams);
		} catch (IllegalStateException | UnsupportedEncodingException e) {
			logger.error("", e);
		}

		Map<String, Object> retMap = doHttpGet(requestUrlPath, requestParams, signatureStr);
		if (retMap.containsKey("error")) {
			logger.warn("error:{}", retMap.get("error"));
		}
		return retMap;
	}

	public Map<String, Object> executePayment(int timestamp, String nonce, String accessToken, int appId,
			String openId, int clientType, int appServerId, int appRoleId, int pointAmount, String appTxnId,
			int itemId, int itemQuantity, String clientIp) {
		String requestUrlPath = "/app/point/execute_payment";
		// 发送请求参数
		String requestParams = String
				.format("timestamp=%1$d&nonce=%2$s&app_id=%3$d&open_id=%4$s&access_token=%5$s&client_type=%6$d&app_server_id=%7$d&app_role_id=%8$d&point_amount=%9$d&app_txn_id=%10$s&item_id=%11$d&item_quantity=%12$d&ip=%13$s",
						timestamp, nonce, appId, openId, accessToken, clientType, appServerId, appRoleId, pointAmount,
						appTxnId, itemId, itemQuantity, clientIp);
		// 生成签名 Signature = HEX(HMACSHA256(Key=payment_key, Message=http_body))
		String signatureStr = "";
		try {
			signatureStr = CryptoUtils.getInstance().hmacSha256ToHex(requestParams);
		} catch (IllegalStateException | UnsupportedEncodingException e) {
			logger.error("", e);
		}

		Map<String, Object> retMap = doHttpPost(SDKConstants.getGarenaUrlBase() + requestUrlPath, requestParams,
				signatureStr);
		if (retMap.containsKey("error")) {
			logger.warn("error:{}", retMap.get("error"));
		}
		return retMap;
	}

	public Map<String, Object> sendPoint(int timestamp, String nonce, String accessToken, int appId, String openId,
			int clientType, int appServerId, int appRoleId, int pointAmount, String appTxnId, int eventId) {
		String requestUrlPath = "/app/point/send";
		// 发送请求参数
		String requestParams = String
				.format("timestamp=%1$d&nonce=%2$s&app_id=%3$d&open_id=%4$s&access_token=%5$s&client_type=%6$d&app_server_id=%7$d&app_role_id=%8$d&point_amount=%9$d&app_txn_id=%10$s&event_id=%11$d",
						timestamp, nonce, appId, openId, accessToken, clientType, appServerId, appRoleId, pointAmount,
						appTxnId, eventId);
		// 生成签名 Signature = HEX(HMACSHA256(Key=payment_key, Message=http_body))
		String signatureStr = "";
		try {
			signatureStr = CryptoUtils.getInstance().hmacSha256ToHex(requestParams);
		} catch (IllegalStateException | UnsupportedEncodingException e) {
			logger.error("", e);
		}

		Map<String, Object> retMap = doHttpPost(SDKConstants.getGarenaUrlBase() + requestUrlPath, requestParams,
				signatureStr);
		if (retMap.containsKey("error")) {
			logger.warn("error:{}", retMap.get("error"));
		}
		return retMap;
	}

	/**
	 * 发送消息推送
	 * 
	 */
	public Map<String, Object> sendNotification(int timestamp, String nonce, int appId, String openId, String message) {
		String requestUrlPath = "/api/notification/send/account";

		// 发送请求参数
		String requestParams = String.format(
				"nonce=%1$s&timestamp=%2$d&app_id=%3$d&account_list=%4$s&message=%5$s&data=HoN", nonce, timestamp,
				appId, openId, message);
		// 生成签名 Signature = HEX(HMACSHA256(Key=payment_key, Message=http_body))
		String[] param3 = new String[] { "POST", SDKConstants.getBeetalkUrlBase() + requestUrlPath, requestParams };
		String requestParamsForSign = StringUtils.join(param3, '|');
		String signatureStr = "";
		try {
			signatureStr = CryptoUtils.getInstance().hmacSha256ToHexForPost(requestParamsForSign);
		} catch (IllegalStateException | UnsupportedEncodingException e) {
			logger.error("", e);
		}

		Map<String, Object> retMap = doHttpPost(SDKConstants.getBeetalkUrlBase() + requestUrlPath, requestParams,
				signatureStr);
		if (retMap.containsKey("error")) {
			logger.warn("error:{}", retMap.get("error"));
		}
		return retMap;
	}

	private Map<String, Object> doHttpGet(String requestUrlPath, String requestParams, String signatureStr) {
		String result = "";
		BufferedReader in = null;
		try {
			String urlNameString = SDKConstants.getGarenaUrlBase() + requestUrlPath;
			URL realUrl = new URL(urlNameString + "?" + requestParams);
			logger.info("open connection!,url={}://{}{}", realUrl.getProtocol(), realUrl.getHost(), realUrl.getPath());
			// 打开和URL之间的连接
			HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
			// 设置通用的请求属性
			connection.setConnectTimeout(SDKConstants.CONNECTION_TIMEOUT);
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			if (StringUtils.isNotBlank(signatureStr)) {
				connection.setRequestProperty("Authorization", "Signature " + signatureStr);
			}
			// GET请求
			connection.setRequestMethod("GET");
			// 建立实际的连接
			connection.connect();
			logger.info("send request,url={}://{}{}", realUrl.getProtocol(), realUrl.getHost(), realUrl.getPath());
			// 定义 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
			return extractPostParams(result);
		} catch (Exception e) {
			logger.error("发送POST请求出现异常！", e);
		}
		// 使用finally块来关闭输入流
		finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e2) {
				logger.error("关闭http请求出现异常！", e2);
			}
		}
		return null;
	}

	private Map<String, Object> doHttpPost(String requestUrlPath, String requestParams, String signatureStr) {
		String result = "";
		BufferedReader in = null;
		PrintWriter out = null;
		try {
			String urlNameString = requestUrlPath;
			URL realUrl = new URL(urlNameString);

			// 打开和URL之间的连接
			HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
			// 设置通用的请求属性
			connection.setConnectTimeout(SDKConstants.CONNECTION_TIMEOUT);
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			if (StringUtils.isNotBlank(signatureStr)) {
				connection.setRequestProperty("Authorization", "Signature " + signatureStr);
			}
			// 发送POST请求必须设置如下两行
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setRequestMethod("POST");
			// Post 请求不能使用缓存
			connection.setUseCaches(false);
			// 获取URLConnection对象对应的输出流
			out = new PrintWriter(connection.getOutputStream());
			// 发送请求参数
			out.print(requestParams);
			// flush输出流的缓冲
			out.flush();

			// 建立实际的连接
			connection.connect();
			logger.info("send request,url={}://{}{},requestParams={}", realUrl.getProtocol(), realUrl.getHost(),
					realUrl.getPath(), requestParams);
			// 定义 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
			return extractPostParams(result);
		} catch (Exception e) {
			logger.error("发送POST请求出现异常！", e);
		}
		// 使用finally块来关闭输入流
		finally {
			try {
				if (in != null) {
					in.close();
				}
				if (out != null) {
					out.close();
				}
			} catch (Exception e2) {
				logger.error("关闭http请求出现异常！", e2);
			}
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Map<String, Object> extractPostParams(String queryString) {
		// URLDecoder.decode(queryString, "UTF-8");
		ObjectMapper mapper = new ObjectMapper();
		Map quizAnswer = null;
		try {
			quizAnswer = mapper.readValue(queryString, Map.class);
		} catch (IOException e) {
			logger.error("", e);
		}
		return quizAnswer;
	}

	private String buildPostParams(Map<String, String> requestParam) {
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (Map.Entry<String, String> aEntry : requestParam.entrySet()) {
			if (i > 0) {
				sb.append("&");
			}
			sb.append(aEntry.getKey() + "=" + aEntry.getValue());
			i++;
		}
		return sb.toString();
	}

}
